// locutor command line client
// (c) 2009, Normen Müller, normen.mueller@gmail.com  
// $Id: Locutor.scala 3 2009-11-06 19:37:06Z normen.mueller $
package locutor.cli

import java.io.File

import scaup.Views._
import scaup.util.Timer

/** 
 * A document model ℳ = <Ḡ, ℰ, ℛ> is a triple constituted of
 *   o a dependency graph model Ḡ = <I, D>.
 *       Ḡ is a directed, classified, labeled multi-graph, where
 *       I is the set of INFOMs and
 *       D ⊆ I X I is the set of dependencies both classified.
 *   o a set of equiv-relations ℰ on Ḡ(I) (Note: declarative syntax not yet implemened)
 *   o a rule system ℛ = <ℙ, ℂ> on Ḡ
 *     ℙ denotes a set of propagation rules on Ḡ and
 *     ℂ a set of consistency rules for Ḡ.
 * 
 * A dependency graph instance d: Ḡ
 * 
 * Change Impact Analysis (CIA):
 * 
 * For a rule system R being applicable for rippling (↩) differrences δ into a dependency graph D, 
 * the graph model G(R) has to be a subset of the graph model G(D), short G(R) ⊑ G(D).
 * 
 * @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 * @version 0.0.1 */
object Locutor {
  // timing
  private val diffTimer: Timer = new Timer
  private var dgTimer: Timer = new Timer
  private var rsTimer: Timer = new Timer
  private var rippleTimer: Timer = new Timer
  
  def main(args: Array[String]) {
    
    // =============================================================== parametes
    val depFile   = args(0).toFile
    val equivFile = args(1).toFile
    val ruleFile  = args(2).toFile
    val oldFile   = args(3).toFile
    val newFile   = args(4).toFile
    
    // =============================================================== DIFF
    //@future declarative syntax for equivalence systems
    //val E = loadEquivalenceSystem(equivFile)
    val Δ = for(δ <- diff(oldFile, newFile) withEquivs locutor.cli.eq.SVNBookEquivs) yield δ match {
      case command =>
        val buf = new StringBuilder
        buf append "# " append command.prefix
        buf append " at " append command.xpath
        println(buf)
        δ
    }
    for(i <- 1 to 150) print("-"); print("\n")
    
    // =============================================================== KB       
    val Ｄ = loadDependencyGraph(depFile)      
    val ℛ = loadRuleSystem(ruleFile)
    // =============================================================== CIA
    
    print("    Rippling...")
    rippleTimer.start
                            val ℒ = Ｄ ↩ Δ having ℛ
    rippleTimer.stop
    println("                             Done ("+rippleTimer+").")
    println("Done ("+((dgTimer.elapsed+rsTimer.elapsed+rippleTimer.elapsed) / 1000.0)+" sec).\n")

    // flushing long-range effects
    println("Impacts:")
    for(ℓ <- ℒ) ℓ match {
      case (command, impact) =>
        val buf = new StringBuilder
        buf append "Command ``" append command.prefix append "'' "
        buf append "at ``" append command.xpath append "'' "
        buf append "has implicit effects on:\n"
        (buf /: (impact.V filter (_.label("effect") == "implicit")))((b, node) => {
          b append "-> " append node.id append ":" append node.label("type")
          b append " at " append node.url append node.xpath 
        })   
        println(buf)
    } 
  } 

  private def diff(oldFile: File, newFile: File) = new {
    import scalaxx.diff.Differ.{xmldiff}
    def withEquivs(equivs: scaup.EquivalenceSystem[scala.xml.Node]) = {
      print("Differencing...")
      diffTimer.start
      val delta = xmldiff(equivs, oldFile, newFile)
      diffTimer.stop
      println("                             Done ("+diffTimer+").")
      println("\nDifferences:")
      delta
    }
  }
  
  private def loadDependencyGraph(description: File) = {
    println("Change Impact Analysis...")
    print("    Loading dependency graph...")
    dgTimer.start    
    val dg = locutor.model.DependencyGraph.parse(description)    
    dgTimer.stop
    println("             Done ("+dgTimer+").")       
    dg
  }
  
  private def loadRuleSystem(description: File) = {
    print("    Loading rule system...")
    rsTimer.start    
    val rs = locutor.model.RuleSystem.parse(description)    
    rsTimer.stop
    println("                  Done ("+rsTimer+").")      
    rs
  }
  
}
